/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"

#if CC_PolicyPCR  // Conditional expansion of this file

#include "PolicyPCR_fp.h"
#include "Marshal.h"


/*(See part 3 specification)
// Add a PCR gate for a policy session
*/
//  Return Type: TPM_RC
//      TPM_RC_VALUE          if provided, 'pcrDigest' does not match the
//                            current PCR settings
//      TPM_RC_PCR_CHANGED    a previous TPM2_PolicyPCR() set
//                            pcrCounter and it has changed
/*
tpm2_policypcr(1) - Generates a PCR policy event with the TPM. A PCR policy event creates a policy bound to 
specific PCR values and is useful within larger policies constructed using policyor and policyauthorize events. 
See tpm2_policyor(1) and tpm2_policyauthorize(1) respectively for their usages. The PCR data factored into the policy 
can be specified in one of 3 ways: 
1. A file containing a concatenated list of PCR values as in the output from tpm2_pcrread. 
2. Requiring the PCR values be read off the TPM by not specifying a PCR file input. 
3. The digest of all the PCR values directly specified as an argument.

tpm2_policypcr(1) - 使用 TPM 生成 PCR 策略事件。 PCR 策略事件创建绑定到特定 PCR 值的策略，并且在使用 policyor 和 policyauthorize 事件构建的更大策略中很有用。 
分别参见 tpm2_policyor(1) 和 tpm2_policyauthorize(1) 了解它们的用法。 策略中考虑的 PCR 数据可以通过以下 3 种方式之一指定：
1. 包含 PCR 值串联列表的文件，如 tpm2_pcrread 的输出中所示。 
2. 要求通过不指定 PCR 文件输入从 TPM 读取 PCR 值。 
3. 直接指定为参数的所有PCR值的摘要。

This command is used to cause conditional gating of a policy based on PCR. This command together with TPM2_PolicyOR() allows 
one group of authorizations to occur when PCR are in one state and a different set of authorizations when the PCR are in a 
different state.
此命令用于导致基于 PCR 的策略的条件门控。 此命令与 TPM2_PolicyOR() 一起允许在 PCR 处于一种状态时发生一组授权，而在 PCR 处于不同状态时发生一组不同的授权
The TPM will modify the pcrs parameter so that bits that correspond to unimplemented PCR are CLEAR.
If policySession is not a trial policy session, the TPM will use the modified value of pcrs to select PCR values to hash 
according to TPM 2.0 Part 1, Selecting Multiple PCR. 
The hash algorithm of the policy session is used to compute a digest (digestTPM) of the selected PCR. If pcrDigest does not 
have a length of zero, then it is compared to digestTPM; and if the values do not match, the TPM shall return TPM_RC_VALUE and 
make no change to policySession→policyDigest. If the values match, or if the length of pcrDigest is zero, then 
policySession→policyDigest is extended by:
policyDigestnew ≔ HpolicyAlg(policyDigestold || TPM_CC_PolicyPCR || pcrs || digestTPM)

TPM 将修改 pcrs 参数，以便与未实现的 PCR 相对应的位是 CLEAR。
如果 policySession 不是试用策略会话，则 TPM 将根据 TPM 2.0 第 1 部分，选择多个 PCR，使用 pcrs 的修改值来选择要散列的 PCR 值。
策略会话的哈希算法用于计算所选 PCR 的摘要 (digestTPM)。 如果 pcrDigest 的长度不为零，则将其与 digestTPM 进行比较； 如果值不匹配，TPM 
将返回 TPM_RC_VALUE 并且不更改 policySession→policyDigest。 如果值匹配，或者 pcrDigest 的长度为零，则 policySession→policyDigest 
扩展为：
policyDigestnew ≔ HpolicyAlg(policyDigestold || TPM_CC_PolicyPCR || pcrs || digestTPM)

*/
TPM_RC
TPM2_PolicyPCR(
    PolicyPCR_In    *in             // IN: input parameter list
    )
{
    SESSION         *session;
    TPM2B_DIGEST     pcrDigest;
    BYTE             pcrs[sizeof(TPML_PCR_SELECTION)];
    UINT32           pcrSize;
    BYTE            *buffer;
    TPM_CC           commandCode = TPM_CC_PolicyPCR;
    HASH_STATE       hashState;

// Input Validation

    // Get pointer to the session structure
    session = SessionGet(in->policySession);

    // Compute current PCR digest
    // session->authHashAlg 使用策略会话设置的算法 
    PCRComputeCurrentDigest(session->authHashAlg, &in->pcrs, &pcrDigest);

    // Do validation for non trial session
    if(session->attributes.isTrialPolicy == CLEAR)
    {
        // Make sure that this is not going to invalidate a previous PCR check
        if(session->pcrCounter != 0 && session->pcrCounter != gr.pcrCounter)
            return TPM_RC_PCR_CHANGED;

        // If the caller specified the PCR digest and it does not
        // match the current PCR settings, return an error..
        if(in->pcrDigest.t.size != 0)
        {
            if(!MemoryEqual2B(&in->pcrDigest.b, &pcrDigest.b))
                return TPM_RCS_VALUE + RC_PolicyPCR_pcrDigest;
        }
    }
    else
    {
        // For trial session, just use the input PCR digest if one provided
        // Note: It can't be too big because it is a TPM2B_DIGEST and the size
        // would have been checked during unmarshaling
        // 如果外部提供了，就使用外部的，否则，使用当前计算得出的
        if(in->pcrDigest.t.size != 0)
            pcrDigest = in->pcrDigest;
    }
// Internal Data Update
    // Update policy hash
    // policyDigestnew = hash(   policyDigestold || TPM_CC_PolicyPCR
    //                      || PCRS || pcrDigest)
    //  Start hash
    CryptHashStart(&hashState, session->authHashAlg);

    //  add old digest
    CryptDigestUpdate2B(&hashState, &session->u2.policyDigest.b);

    //  add commandCode
    CryptDigestUpdateInt(&hashState, sizeof(TPM_CC), commandCode);

    //  add PCRS
    buffer = pcrs;
    pcrSize = TPML_PCR_SELECTION_Marshal(&in->pcrs, &buffer, NULL);
    CryptDigestUpdate(&hashState, pcrSize, pcrs);

    //  add PCR digest
    CryptDigestUpdate2B(&hashState, &pcrDigest.b);

    //  complete the hash and get the results
    CryptHashEnd2B(&hashState, &session->u2.policyDigest.b);

    //  update pcrCounter in session context for non trial session
    if(session->attributes.isTrialPolicy == CLEAR)
    {
        session->pcrCounter = gr.pcrCounter;
    }

    return TPM_RC_SUCCESS;
}

#endif // CC_PolicyPCR